﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Services;
using System.Reflection;
using ADONETDataServicesExtension.Providers;
using ADONETDataServicesExtension.Utils;
using System.Collections;

namespace ADONETDataServicesExtension
{
    public static class RequestUriProcessor
    {
        internal const string FrameworkTypeName = "System.Data.Services.RequestUriProcessor";

        public static Uri GetAbsoluteRequestUri(IDataServiceHost host)
        {
            return ReflectionHelper.InvokeStaticMethod<Uri>(FrameworkTypeName, "GetAbsoluteRequestUri", host);
        }

        public static Uri GetServiceUri(IDataServiceHost host)
        {
            return ReflectionHelper.InvokeStaticMethod<Uri>(FrameworkTypeName, "GetServiceUri", host);
        }

        public static RequestDescription ProcessRequestUri(Uri absoluteRequestUri, IDataService service)
        {
            RequestDescription description;
            string[] segments = EnumerateSegments(absoluteRequestUri, service.Host.AbsoluteServiceUri);
            if (segments.Length > 100)
                DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_TooManySegments).Throw();
            SegmentInfo[] segmentInfos = CreateSegments(segments, service);
            SegmentInfo info = (segmentInfos.Length == 0) ? null : segmentInfos[segmentInfos.Length - 1];
            int targetKind = (info == null) ? RequestTargetKind.ServiceDirectory : info.TargetKind;
            Uri resultUri = GetResultUri(service.Host);
            if (targetKind == RequestTargetKind.Metadata || targetKind == RequestTargetKind.Batch || targetKind == RequestTargetKind.ServiceDirectory)
                description = new RequestDescription(targetKind, RequestTargetSource.None, resultUri);
            else if (targetKind == RequestTargetKind.VoidServiceOperation)
            {
                    info.Operation.Method.Invoke(service, info.OperationParameters);
                    description = new RequestDescription(RequestTargetKind.VoidServiceOperation, RequestTargetSource.ServiceOperation, resultUri);
            }
            else
            {
                int targetSource = info.TargetSource;
                ResourceProperty projectedProperty = info.ProjectedProperty;
                string containerName = (info.TargetKind != RequestTargetKind.PrimitiveValue) ? info.Identifier : segmentInfos[segmentInfos.Length - 2].Identifier;
                bool usesContainerName = (((targetSource == RequestTargetSource.Property) && (projectedProperty != null)) && (projectedProperty.Kind != ResourcePropertyKind.ResourceSetReference)) || ((targetSource == RequestTargetSource.ServiceOperation) && (info.Operation.ResultKind == ServiceOperationResultKind.QueryWithSingleResult));
                string mimeType = ((targetSource == RequestTargetSource.Property) && (projectedProperty != null)) ? projectedProperty.MimeType : ((targetSource == RequestTargetSource.ServiceOperation) ? info.Operation.MimeType : null);
                description = new RequestDescription(segmentInfos, containerName, usesContainerName, mimeType, resultUri);
            }
            return RequestQueryProcessor.ProcessQuery(service, description);
        }

        private static string[] EnumerateSegments(Uri absoluteRequestUri, Uri baseUri)
        {
            return ReflectionHelper.InvokeStaticMethod<string[]>(FrameworkTypeName, "EnumerateSegments", absoluteRequestUri, baseUri);
        }

        private static SegmentInfo[] CreateSegments(string[] segments, IDataService service)
        {
            return ReflectionHelper.InvokeStaticMethod<IEnumerable>(FrameworkTypeName, "CreateSegments", segments, service.FrameworkObject).Cast<object>().Select(si => new SegmentInfo(si)).ToArray();
        }

        public static Uri GetResultUri(IDataServiceHost host)
        {
            return ReflectionHelper.InvokeStaticMethod<Uri>(FrameworkTypeName, "GetResultUri", host);
        }
    }
}
